
// 通常，当你的组件需要“跳出” React 并与外部 API 通信时，你会用到 ref —— 通常是不会影响组件外观的浏览器 API。以下是这些罕见情况中的几个：

//     存储 timeout ID
//     存储和操作 DOM 元素，我们将在 下一页 中介绍
//     存储不需要被用来计算 JSX 的其他对象。

// 如果你的组件需要存储一些值，但不影响渲染逻辑，请选择 ref。


// ref 是一种脱围机制，用于保留不用于渲染的值。 你不会经常需要它们。
// ref 是一个普通的 JavaScript 对象，具有一个名为 current 的属性，你可以对其进行读取或设置。
// 你可以通过调用 useRef Hook 来让 React 给你一个 ref。
// 与 state 一样，ref 允许你在组件的重新渲染之间保留信息。
// 与 state 不同，设置 ref 的 current 值不会触发重新渲染。
// 不要在渲染过程中读取或写入 ref.current。这使你的组件难以预测。

import { useState, useRef, forwardRef, useEffect } from 'react';

// function MyInput(props) {
//     return <input {...props} />;
// }
// <MyInput ref={inputRef} /> 告诉 React 将对应的 DOM 节点放入 inputRef.current 中。但是，这取决于 MyInput 组件是否允许这种行为， 默认情况下是不允许的。
// MyInput 组件是使用 forwardRef 声明的。 这让从上面接收的 inputRef 作为第二个参数 ref 传入组件，第一个参数是 props 。
// MyInput 组件将自己接收到的 ref 传递给它内部的 < input >。

// const MyInput = forwardRef((props, ref) => {
//     return <input {...props} ref={ref} />;
// });

// 上面的例子中，MyInput 暴露了原始的 DOM 元素 input。这让父组件可以对其调用focus()。然而，这也让父组件能够做其他事情 ——
// 例如，改变其 CSS 样式。在一些不常见的情况下，你可能希望限制暴露的功能。你可以用 useImperativeHandle 做到这一点：
const MyInput = forwardRef((props, ref) => {
    const realInputRef = useRef(null);
    useImperativeHandle(ref, () => ({
        // 只暴露 focus，没有别的
        focus() {
            realInputRef.current.focus();
        },
    }));
    return <input {...props} ref={realInputRef} />;
});
export default function Chat() {
    const [text, setText] = useState('');
    const inputRef = useRef(null);

    function handleClick() {
        inputRef.current.focus();
    }
    function handleSend() {
        setTimeout(() => {
            alert('正在发送：' + text);
        }, 3000);
    }
    const firstCatRef = useRef(null);
    const secondCatRef = useRef(null);
    const thirdCatRef = useRef(null);
    function handleScrollToFirstCat() {
        firstCatRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'center'
        });
    }

    function handleScrollToSecondCat() {
        secondCatRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'center'
        });
    }

    function handleScrollToThirdCat() {
        thirdCatRef.current.scrollIntoView({
            behavior: 'smooth',
            block: 'nearest',
            inline: 'center'
        });
    }

    const inputRef0 = useRef(null);

    function handleClick() {
        inputRef0.current.focus();
    }


    // 可以强制 React 同步更新（“刷新”）DOM。 为此，从 react-dom 导入 flushSync 并将 state 更新包裹 到 flushSync 调用中：
    setTodos([...todos, newTodo]);
    listRef.current.lastChild.scrollIntoView();

    // 可以强制 React 同步更新（“刷新”）DOM。 为此，从 react-dom 导入 flushSync 并将 state 更新包裹 到 flushSync 调用中：
    flushSync(() => {
        setTodos([...todos, newTodo]);
    });
    listRef.current.lastChild.scrollIntoView();

    // 当 VideoPlayer 组件渲染时（无论是否为首次渲染），都会发生以下事情。首先，React 会刷新屏幕，确保 <video> 元素已经正确地出现在 DOM 中；\
    // 然后，React 将运行 Effect；最后，Effect 将根据 isPlaying 的值调用 play() 或 pause()。
    // 一般来说，Effect 会在 每次 渲染时执行。但更多时候，并不需要每次渲染的时候都执行 Effect。
    useEffect(() => {
        if (isPlaying) {  // isPlaying 在此处使用
            ref.current.play();
        } else {
            ref.current.pause();
        }
    }, [isPlaying]); // ……所以它必须在此处声明！
    // 没有依赖数组作为第二个参数，与依赖数组位空数组 [] 的行为是不一致的：
    useEffect(() => {
        // 这里的代码会在每次渲染后执行
    });

    useEffect(() => {
        // 这里的代码只会在组件挂载后执行
    }, []);

    useEffect(() => {
        //这里的代码只会在每次渲染后，并且 a 或 b 的值与上次渲染不一致时执行
    }, [a, b]);

    useEffect(() => {
        const connection = createConnection();
        connection.connect();
        return () => { // 在 Effect 中返回一个 清理（cleanup） 函数。
            connection.disconnect();
        };
    }, []);

    // 订阅事件   如果 Effect 订阅了某些事件，清理函数应该退订这些事件：
    useEffect(() => {
        function handleScroll(e) {
            console.log(window.scrollX, window.scrollY);
        }
        window.addEventListener('scroll', handleScroll);
        return () => window.removeEventListener('scroll', handleScroll);
    }, []);
    // 触发动画  如果 Effect 对某些内容加入了动画，清理函数应将动画重置：
    useEffect(() => {
        const node = ref.current;
        node.style.opacity = 1; // 触发动画
        return () => {
            node.style.opacity = 0; // 重置为初始值
        };
    }, []);

    const inputRef1 = useRef(null);
    const SearchInput = forwardRef((props, ref) => {
        const realInputRef = useRef(null);
        useImperativeHandle(ref, () => ({
            // 只暴露 focus，没有别的
            focus() {
                realInputRef.current.focus();
            },
        }));
        return <input {...props} ref={realInputRef} />;
    });
    function SearchButton({ onClick }) {
        return (
            <button onClick={onClick}>
                搜索
            </button>
        );
    }
    return (
        <>
            <nav>
                <SearchButton onClick={() => {
                    inputRef1.current.focus();
                }} />
            </nav>
            <SearchInput ref={inputRef1} />


            <video ref={ref} src={src} loop playsInline />;
            <MyInput ref={inputRef0} />
            <button onClick={handleClick}>
                聚焦输入框
            </button>


            <nav>
                <button onClick={handleScrollToFirstCat}>
                    Tom
                </button>
                <button onClick={handleScrollToSecondCat}>
                    Maru
                </button>
                <button onClick={handleScrollToThirdCat}>
                    Jellylorum
                </button>
            </nav>
            <div>
                <ul>
                    <li>
                        <img
                            src="https://placekitten.com/g/200/200"
                            alt="Tom"
                            ref={firstCatRef}
                        />
                    </li>
                    <li>
                        <img
                            src="https://placekitten.com/g/300/200"
                            alt="Maru"
                            ref={secondCatRef}
                        />
                    </li>
                    <li>
                        <img
                            src="https://placekitten.com/g/250/200"
                            alt="Jellylorum"
                            ref={thirdCatRef}
                        />
                    </li>
                </ul>
            </div>
            <input ref={inputRef} />
            <button onClick={handleClick}>
                聚焦输入框
            </button>
            <input
                value={text}
                onChange={e => setText(e.target.value)}
            />
            <button
                onClick={handleSend}>
                发送
            </button>
        </>
    );
}


/// import { useEffect, useRef } from 'react';

// export default function MyInput({ value, onChange }) {
//   const ref = useRef(null);

//   useEffect(() => {
//     ref.current.focus();
//   }, []);

//   return (
//     <input
//       ref={ref}
//       value={value}
//       onChange={onChange}
//     />
//   );
// }



// import { useState, useEffect } from 'react';

// export default function Counter() {
//     const [count, setCount] = useState(0);

//     useEffect(() => {
//         function onTick() {
//             setCount(c => c + 1);
//         }

//         const intervalId = setInterval(onTick, 1000);
//         return () => clearInterval(intervalId);
//     }, []);

//     return <h1>{count}</h1>;
// }

//
// import { useState, useEffect } from 'react';
// import { fetchBio } from './api.js';

// export default function Page() {
//     const [person, setPerson] = useState('Alice');
//     const [bio, setBio] = useState(null);
//     useEffect(() => {
//         let ignore = false;
//         setBio(null);
//         fetchBio(person).then(result => {
//             if (!ignore) {
//                 setBio(result);
//             }
//         });
//         return () => {
//             ignore = true;
//         }
//     }, [person]);

//     return (
//         <>
//             <select value={person} onChange={e => {
//                 setPerson(e.target.value);
//             }}>
//                 <option value="Alice">Alice</option>
//                 <option value="Bob">Bob</option>
//                 <option value="Taylor">Taylor</option>
//             </select>
//             <hr />
//             <p><i>{bio ?? '加载中……'}</i></p>
//         </>
//     );
// }